perm filename TCPIP.MAC[IP,SYS] blob
sn#680221 filedate 1982-10-14 generic text, type T, neo UTF8
;CWL:<403-TCP>TCPIP.MAC.40303 6-May-82 15:55:31, Edit by CLYNN
; Work on windowing; newer round-trip-time algorithm
; Fix bad port numbers in SNDRST
;<403-TCP>TCPIP.MAC.40302 9-Mar-82 20:28:46, Edit by CLYNN
; Updated for TCP release 3
;[BBNF]<401-TCP>TCPIP.MAC.153, 21-Jul-81 11:37:00, Ed: CLYNN
; Fix: "No such TCB" check in INPRO4, TCP data test in PRCSY3,
; RX queue check at REMSE7, RSTADR, Treat RST to persistent
; open (w/timeout) as error at PRCRS1
;<MNET-TCP>TCPIP.MAC.2, 4-Apr-81 17:11:08, Edit by TAPPAN
; merge with multinet stuff
SEARCH INPAR,TCPPAR,PROLOG
TTITLE TCPIP
SUBTTL TCP Input Processor, Willaim W. Plummer, 27Jan77
SWAPCD
COMMENT ! Executed in Internet (JOB0) context
The INPUTPROCESSOR is called to process the queue
of packets just input from the network. Each packet is checked
for proper format, protocol version, checksum, etc and if
all is OK, the correct TCB is looked up. IP may respond
with an RST packet if the TCB is not there, it may ACK
the packet if it is a duplicate, or it may process some
things from the packet and queue it for the REASSEMBLER.
In particular, IP processes the data acknowledged by the
packet and sets the new window for PZ.
* INPROC ... 3 ...... Scan queue. Check packet. Lookup TCB.
INPUT .... 6 ...... 2nd phase. Called with valid TCB, PKT
CHKSEQ ... 8 ...... Determine if packet should be processes
CKSYNS ... 9 ...... Check sequence when synchronized
CKACKS ... 10 ...... Check sequence when getting synchonized
PRCPKT ... 11 ...... Process the packet
REMCHK ... 15 ...... See if packet can be removed from RA queue
PRCRST ... 16 ...... Process RST packet
PRCWND ... 17 ...... Process Window information
* PRCACK ... 19 ...... Process ACK
PRCURG ... 23 ...... Process URG
PRCSYN ... 24 ...... Process SYN
SNDRST ... 25 ...... Send and RST packet
* REMSEQ ... 27 ...... Delete packets from queue between two limits
* ABTCON ... 30 ...... Abort a connection
ABTCO1 ... 32 ...... Scan TCP buffer done queue for ABTCON
SYNAGN ... 33 ...... Put connection back in syncable state
RSTADR ... 35 ...... Restore wild foreign adr fields after false start
IPINI ... 36 ...... Initialize Input Processor Process block
!
; Process the input packet queue.
; Packets have been queue by IMPDV interrupt level.
; CALL INPROC
;Ret+1: always.
INPROC::STACKL <<ARGBLK,CHKADW>>
CHKADL ; Room for args to CHKADD
; PUSH P,TCB ; Higher levels don't care
PUSH P,PKT
PUSH P,TPKT
; Top of main loop. Get next packet to be processed.
INPRO0: MOVE T1,TCPIPQ ; Get pointer to input queue head
;;; NOSKED ; Interlock against Internet process
LOAD PKT,QNEXT,(T1) ; Get pointer to first thing on queue
CAIN PKT,0(T1) ; If that is the head itself
JRST INPROX ; Get out because it is empty (need OKSKED)
SETSEC PKT,INTSEC ; Make extended address
MOVE T1,PKT ; What to dequeue
CALL DQ ; Remove from input queue
;;; OKSKED
SETZ TCB, ; May be a bad packet
MOVX T1,PT%TDI ; TCP received packet
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
LOAD T1,PIDO,(PKT) ; Internet Data Offset
XMOVEI TPKT,PKTELI(PKT) ; Pointer to Internet packet
ADD TPKT,T1 ; Pointer to TCP packet
CALL TCPCKS ; Compute checksum function
JUMPE T1,INPRO2 ; Jump if good
; Packet has bad checksum. Flush it.
;INPRO1:
AOS BADPCT ; Count bad packets
MOVX T1,PT%XX5 ; Code for "Flushed by IP"
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
MOVX T1,PT%TKC ; TCP Killed due to bad checksum
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
CALL RETPKT ; Return the packet storage
JRST INPRO0 ; And hope for better luck next Pkt
; Packet is OK to process ...
INPRO2:
REPEAT 0,<
SKIPN STATF ; Taking statistics right now?
JRST INPRO4 ; No
CALL ONLCLT ; Source is using our time base too?
JUMPE T1,INPRO3 ; Cannot process its timestamp if not.
CALL GETTSO ; Get the timestamp from the option
STOR T1,PTS,(PKT) ; Store in better part of packet
MOVEI T1,IPDLAY ; Select delay histogram
CALL TSTAMP ; Process and reset the timestamp
JRST INPRO4
INPRO3: MOVE T1,TODCLK ; Current millisecond
STOR T1,PTS,(PKT) ; Make timestamp OK for subsequent chks
INPRO4:
> ; End of REPEAT 0
CALL PKTEND ; Get seq. number of End of Pkt plus 1
STOR T1,PESEQ,(PKT) ; Keep in handy place
; Now setup for a call to CHKADD which looks up the TCB addressed
; by the pkt. If it is found, CHKADD calls INPUT with said TCB locked.
MOVEI PARAMS,ARGBLK ; Arg area on stack (ref. via sec. 0)
LOAD T1,PIDH,(PKT) ; Destination Host
MOVEM T1,LH
LOAD T2,PDP,(TPKT) ; Destination port
MOVEM T2,LP
LOAD T3,PISH,(PKT) ; Source Host
MOVEM T3,FH
LOAD T4,PSP,(TPKT) ; Source Port
MOVEM T4,FP
SETZM JCN ; No JCN. Call is from IP.
SETOM WILDOK ; Wild TCB (Listen) is OK for match
MOVE T2,[MSEC1,,INPUT] ; Function to call if found
MOVEM T2,FN
SETZM ARG1 ; No ARG1
SETZM ARG2 ; or ARG2
MOVE T1,PARAMS ; Arg block for CHKADD
CALL CHKADD ; Check the address of the packet
CAMN T1,[-1] ; Packet disposed of successfully?
JRST INPRO0 ; Yes. Do another one.
PUSH P,T1
MOVX T1,PT%XX5 ; Code for "Flushed by IP"
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
POP P,T1
JN PRST,(TPKT),INPRO5 ; Don't respond to RST!
HRRZ T2,T1 ; Get just the error portion
CAIE T2,EFP+↑D7 ; Is it "No such TCB" ?
CAIN T2,ELP+↑D7 ; Is it "No such TCB" ?
SETZ TCB,0 ; Yes. TCB used as flag to SNDRST
CALL SNDRST ; Reply with an RST Packet
INPRO5:
; Done with packet.
MOVX T1,PT%TID ; TCP done with packet
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
CALL RETPKT ; Give back the storage
JRST INPRO0 ; And process some more packets
; Packet queue completely processed.
INPROX:;OKSKED
POP P,TPKT
POP P,PKT
; POP P,TCB
CHKADR
RET
; INPUT 2nd phase of InputProcessor. Called via CHKADD from INPROC
;PKT/ (Extended) Internet Packet Pointer
;TPKT/ (Extended) TCP Packet Pointer
;TCB/ (Extended) Locked connection block
;
; CALL INPUT
;Ret+1: Always. T1 has -1 if Pkt was handled OK,
; 0 if Pkt was on a closed (NOTSYN,NOTSYN) connection,
; -1,,EFP+↑D7
INPUT: LOCAL <TESTB>
LOAD T1,TSSYN,(TCB) ; State of Send synchronization
LOAD T2,TRSYN,(TCB) ; State of Recv synchronization
CAIN T1,NOTSYN
CAIE T2,NOTSYN
CAIA ; Still open in some respect
JRST INPUTF ; Packet on closed connection.
CALL CHKSEQ ; See if properly sequenced
MOVEM T1,TESTB ; -1 OK or DUP,,0; IGN,,0; ERR,,EFP+↑D7
; (ReSync)
HLRZ T2,TESTB
CAMN TESTB,[-1] ; TRUE ?
JRST INPUT3
CAIN T2,ERR ; ERROR ?
JRST INPUT4
; Packet is a DUP, IGN, (or "other")
; CAIE T2,IGN ; IGNORABLE?
CAIE T2,DUP ; DUPLICATE ?
JRST INPUT2 ; NOTA -- Ignore it
; Duplicate received. (SYN on a synched connection)
AOS DUPKCT ; Count it.
JE PACK,(TPKT),INPUT1 ; No ACK. Forget the following.
JN PRST,(TPKT),INPUT1 ; Don't process ACK from RST packet
LOAD T1,PACKS,(TPKT) ; ACK sequence from Packet
CALL PRCACK ; Process the ACK
INPUT1:
CALL NULPKT ; Does the packet have any contents
SKIPN T1 ; Skip FRCPKT if null
CALL FRCPKT ; Generate an ACK for the DUP
SKIPA T1,[PT%TKD]
; All packets except ERR and good ones come here (ie DUP & IGN)
; Pkt should be IGNored, resyncing connection
INPUT2: MOVX T1,PT%TKR ; TCP Killed, Resyncing
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
MOVX T1,PT%XX6 ; IP code
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
CALL RETPKT ; Release the storage space
JRST INPUTT ; Return true to say it's been handled
; Handle normal, acceptable packet
INPUT3: LOAD T1,PIDH,(PKT) ; Get which incarnation he knows us under
STOR T1,TLH,(TCB) ; Stick it away
CALL PRCPKT ; Process the packet
AOS IPPKCT ; Count as Processed by IP
JRST INPUTT ; and return TRUE
; CHKSEQ said this packet is in error -- no TCB (ERR,,EFP+↑D7)
INPUT4: MOVX T1,PT%XX3 ; IP code
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
MOVX T1,PT%TKX ; TCP Killed, no TCB
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
HRRO T1,TESTB ; -1,,EFP+↑D7 to return
EXIT INPUTX ; (must RETPKT)
; Packet on closed connection (NOTSYN,NOTSYN)
INPUTF: MOVX T1,PT%TKN ; TCP Pkt received on closed connection
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
TDZA T1,T1 ; Packet not processed (must RETPKT)
INPUTT: SETO T1, ; Return TRUE, packet processed & RETPKT'd
INPUTX: RESTORE
RET
; CHKSEQ Determine the validity of a packet on the basis of
; sequence number, acknowledge number, synchronization
; state, and the presence of SYN in the packet.
;PKT/ (Extended) Internet Packet Pointer
;TPKT/ (Extended) TCP Packet Pointer
;TCB/ (Extended) Locked connection block
;
; CALL CHKSEQ
;Ret+1: always with T1 having ERR,,EFP+↑D7; DUP,,0; IGN,,0 or -1 if OK
CHKSEQ: LOAD T1,TRSYN,(TCB) ; Recv state
CAIN T1,SYNABL ; Listening?
JRST CKACKS ; Yes. Validate using ACK sequence
; Fall into CKSYNS
; Fall into CKSYNS from CHKSEQ above
; CKSYNS Check sequence number. Used while receive sync is
; established. A packet sequenced within the receive
; window is accepted; without is considered a duplicate.
; Stray SYNs on already synched connections are called DUPlicates
; and will cause a null packet to be emitted which contains the
; sequence we are sending on and the ACK sequence describing what we
; want to hear next. This is enough information for the other end to
; be able to form an RESET packet which will flush this connection. He
; would do this only if he had restarted recently.
;PKT/ (Extended) Internet Packet Pointer
;TPKT/ (Extended) TCP Packet Pointer
;TCB/ (Extended) Locked connection block
;
; CALL CKSYNS
;Ret+1: always. T1 has -1 if pkt is OK to process, or DUP,,0; IGN,,0
CKSYNS: JE PSYN,(TPKT),CKSYN1 ; Jump if no stray SYN in packet.
LOAD T1,TRSYN,(TCB) ; Get receive state
LOAD T2,PSEQ,(TPKT) ; and sequence number from packet
LOAD T3,TRIS,(TCB) ; and seq. number of original SYN recvd
CAIN T1,SYNRCV ; In SYN-RECEIVED?
CAMN T2,T3 ; or just another copy of orig SYN?
JRST CKSYND ; Call it a dup. to get null pkt sent
CALL SYNAGN ; Resync. the conn. Other end crashed
MOVX T1,<IGN,,0> ; Tell caller to ignore this packet
EXIT CKSYNX
CKSYN1: LOAD T1,TRLFT,(TCB) ; Left window edge
LOAD T2,TRWND,(TCB) ; Width of window
JE PRST,(TPKT),CKSYN2 ; No window diddle if not RST in pkt
SKIPN T3,T2 ; If non-0, use it
MOVX T3,1 ; Otherwise diddle so RST gets done
ADD T3,T1 ; Compute Right plus 1 or width 1 window
MODSEQ T3 ; Keep within right number of bits
LOAD T2,PSEQ,(TPKT) ; Get sequence number of packet
CALL CHKWND ; Is RST within the window?
JRST CKSYN9 ; Go see
CKSYN2: ADD T2,T1 ; Right window edge plus 1
MODSEQ T2
LOAD T3,PSEQ,(TPKT) ; Packet sequence number
LOAD T4,PESEQ,(PKT) ; Sequence number following Pkt
CALL OVRLAP ; Pkt and window have common point(s)?
CKSYN9: SKIPN T1 ; Skip if yes
CKSYND: MOVX T1,<DUP,,0> ; Call it a DUPlicate
CKSYNX: RET ; From CHKSEQ
; CKACKS Check ACK Sequence. Validate pkt when not synchronized.
; When receive synchronization has not been established, the only
; acceptable packet is one which will establish receive synchronization.
; Furthermore, if we have established send synchronization, the packet,
; if it acknowledges anything, must acknowledge something we have
; currently sent (eg, a SYN).
;PKT/ (Extended) Internet Packet Pointer
;TPKT/ (Extended) TCP Packet Pointer
;TCB/ (Extended) Locked connection block
;
; CALL CKACKS
;Ret+1: always. T1 having ERR,,EFP+↑D7 else -1 if pkt is ok to process
CKACKS: JN PACK,(TPKT),CKASE1 ; Jump if packet ACKs something
JE PSYN,(TPKT),CKASE2 ; Give error if no SYN and no ACK
JRST CKASET ; SYN and no ACK. Try to open conn.
CKASE1: LOAD T1,TSSYN,(TCB) ; Get send state
CAIN T1,SYNABL ; Have a Send Sequence to check?
JRST CKASE2 ; Error if not waiting for SYN
; (ACKing something we never sent)
LOAD T1,TSLFT,(TCB) ; Get Send Left
LOAD T2,PACKS,(TPKT) ; What the Packet ACKS
LOAD T3,TSSEQ,(TCB) ; Current Send Sequence
ADDI T3,1
MODSEQ T3
CALL CHKWND ; Does Pkt ACK someting outstanding?
JUMPN T1,CKASET ; Return TRUE if so
CKASE2:
CALL RSTADR ; Restore Wild foreign address fields
SKIPA T1,[ERR,,EFP+↑D7] ; Give error
CKASET: SETO T1, ; Ok to process
RET ; From CHKSEQ
; PRCPKT Process Packet which has been determined to be acceptable
;PKT/ (Extended) Internet Packet Pointer
;TPKT/ (Extended) TCP Packet Pointer
;TCB/ (Extended) Locked connection block
;
; CALL PRCPKT
;Ret+1: always. No value returned. PKT disposed of
PRCPKT: LOCAL <QP,QS> ; OLDR??
; Extract IP & TCP options
CALL TCPXIO ; Extract IP
CALL TCPXTO ; and TCP options
; Process RESET
JE PRST,(TPKT),PRCPK1 ; Jump if not a RESET Packet
CALL PRCRST ; Process the reset (may not have PACK)
MOVX T1,PT%TRS
JRST PRCPKR ; Return to caller.
PRCPK1:
; Process ACK
JE PACK,(TPKT),PRCPK2 ; Does the packet acknowledge anything?
LOAD T1,PACKS,(TPKT) ; Yes. Get the ack sequence
CALL PRCACK ; And process the ACK & update send window
PRCPK2:
; Process Urgent
JE PURG,(TPKT),PRCPK5 ; Contains urgent pointer?
CALL PRCURG ; Yes. Process that.
PRCPK5:
; See if there is anything to process in the packet
; If not return the storage and return to caller.
CALL NULPKT ; See if PKT is null
MOVX T2,PT%TID
EXCH T1,T2
JUMPN T2,PRCPKR ; Jump if so. Return to caller.
; Process SYN
JE PSYN,(TPKT),PRCPK7 ; Jump if no SYN
CALL PRCSYN ; Process the SYN
PRCPK7: ; Sets TRLFT at TRIS+1, R-state changed
; Tell USREVT(OK) if SYN.SYN
; Trim input packet if short on space
CALL TRMPKT ; Trim it size or flush PKT for space
JUMPE PKT,PRCPKX
; Queue the Packet for the Reassembler. The receive packet queue is
; basically ordered by sequence number, but may have partially
; overlapping segments on it. (try searching right to left)
XMOVEI QS,TCBRPQ(TCB) ; Set scan pointer to Reassembly Q head
JE TRPP,(TCB),PRCPK8 ; Partial Pkt contains left. Skip it.
LOAD QS,QNEXT,(QS) ; Get next packet after the partial one.
SETSEC QS,INTSEC ; Make extended address
; Top of the search for right place to insert loop
PRCPK8: LOAD QS,QNEXT,(QS) ; Get ptr to thing after scan pointer
CAIN QS,TCBRPQ(TCB) ; If that is the head, all has been seen
JRST PRCPK9 ; So insert just before the head (= end)
SETSEC QS,INTSEC ; Make extended address
LOAD T3,PIDO,(QS) ; Internet data offset
XMOVEI T4,PKTELI(QS) ; Pointer to Internet portion
ADD T4,T3 ; Pointer to TCP portion
LOAD T1,TRLFT,(TCB) ; Get Recv Left
LOAD T2,PSEQ,(TPKT) ; Sequence of new packet
LOAD T3,PSEQ,(T4) ; Sequence of current pkt on the queue
PUSH P,T3 ; Save sequence number around call
CALL CHKWND ; New pkt fits before this one on queue?
POP P,T3 ; Recover sequence number for use below
JUMPE T1,PRCPK8 ; No. Advance queue scan ptr. Try next.
; Have a likely place to put the packet. Be sure that we will not
; insert before one which contains left.
MOVE T1,T3 ; Sequence number of packet on queue
LOAD T2,TRLFT,(TCB) ; Recv Left
LOAD T3,PESEQ,(QS) ; End of packet + 1
CALL CHKWND ; Left within this packet?
JUMPN T1,PRCPK8 ; Jump if so. Look at next packet.
; Now QS points to where to insert the new packet (before QS). Do
; checking to see if the packet to the left and packets to the
; right are completely contained by the packet being processed.
; Release storage of those which are. This works well if retranmissions
; are equal to- or bigger than- the original transmissions.
; It does not worry about duplicates of (small) original transmissions
; which might be contained by packets already queued.
; N.B. It works to replace a "partial packet" with a bigger one.
PRCPK9: SETSEC QS,INTSEC ; Make extended address
LOAD T1,QPREV,(QS) ; Get one just skipped (or header)
CAIE T1,TCBRPQ(TCB) ; Avoid fiddling with the header
CALL REMCHK ; Check and maybe remove T1 from Q
; (REMCHK does SETSEC)
PRCP9A: HRRZ T1,QS ; Get current insert point
CAIN T1,TCBRPQ(TCB) ; Is that the header?
JRST PRCP10 ; Yes. Don't check that.
MOVE QP,QS ; Save as predecessor in case needed
LOAD QS,QNEXT,(QP) ; Get next item on Q for next time
SETSEC QS,INTSEC ; Make extended address
MOVE T1,QP ; Check the current item
CALL REMCHK ; Maybe delete it
; (REMCHK does SETSEC)
JUMPN T1,PRCP9A ; Jump if deleted (QS ok for next time)
MOVE QS,QP ; Restore QS (point of insertion)
PRCP10:
; Actually queue the packet for the Reassembler.
MOVE T1,PKT ; Select the packet for EnQueueing
MOVE T2,QS ; Where to enqueue it -- before QS.
CALL NQ ; Enqueue the packet
MOVX T1,PT%XX3 ; Code for "processed by IP"
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
MOVX T1,PT%TQR
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
; Now see if the Reassembler has anything to do. Call it if so.
LOAD T4,QNEXT,<+TCBRPQ(TCB)> ; Must be a pkt queued!
SETSEC T4,INTSEC ; Make extended address
LOAD T2,PESEQ,(T4) ; Fetch end sequence for what follows
LOAD T3,PIDO,(T4) ; Internet data offset
XMOVEI T4,PKTELI(T4) ; Pointer to Internet portion
ADD T4,T3 ; Pointer to TCP portion
LOAD T3,PCTL,(T4) ; Get word containing control bits
TXNE T3,<PSYN!PFIN> ; Some kind of control?
JRST PRCP11 ; Yes. RA must process it now.
JN TRCB,(TCB),PRCP11 ; Maybe there is a partially filled buffer
JN TTVT,(TCB),PRCP11 ; No normal buffers for TVTs
LOAD T1,QNEXT,<+TCBRBQ(TCB)> ; Pointer 1st buffer on queue
CAIN T1,TCBRBQ(TCB) ; Empty?
JRST PRCPKX ; Yes. RA cannot do anything.
PRCP11: MOVE T3,T2 ; Recover end sequence
LOAD T1,PSYN,(TPKT) ; If have a SYN, TRLFT already increased
ADD T3,T1 ; so increase right too
MODSEQ T3
LOAD T1,PSEQ,(T4) ; Get sequence number of pkt
LOAD T2,TRLFT,(TCB) ; Recv Left is the point of reassembly
CALL CHKWND ; Did this packet fill the hole?
JUMPE T1,PRCPKX ; Jump if not. No need to run RA.
$SIGNL(RA,0) ; Make Reassembler run now
EXIT PRCPKX
; Here with packet which cannot be processed further. Release storage.
PRCPKR: TDNE T1,INTTRC ; Want trace? (PT%TID,PT%TRS)
CALL PRNPKT ; Yes
MOVX T1,PT%XX6 ; "Flushed by IP" code
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes, Call Packet Printer
CALL RETPKT ; Give back to free storage
PRCPKX: RESTORE
RET
; REMCHK Check whether incoming packet contains a packet on a
; reassembly queue. If so, delete the Q'd packet.
;T1/ NOT EXTENDED pointer to packet on a reassembly queue
;PKT/ (Extended) Internet Packet Pointer (incoming pkt)
;TPKT/ (Extended) TCP Packet Pointer (incoming pkt)
;TCB/ (Extended) Locked connection block
;
; CALL REMCHK
;Ret+1: Always. T1 -1 if packet was deleted or 0 otherwise.
REMCHK: LOCAL <QPKT> ; Holds pointer to queued packet
MOVEM T1,QPKT
SETSEC QPKT,INTSEC ; Make extended address
LOAD T3,PIDO,(QPKT) ; Number of words of IN header
XMOVEI T4,PKTELI(QPKT) ; Pointer to Internet portion
ADD T4,T3 ; Pointer to TCP portion
LOAD T1,PSEQ,(T4) ; Start of queued packet
LOAD T2,PESEQ,(QPKT) ; End + 1 of queued packet
LOAD T3,PESEQ,(PKT) ; End + 1 of Pkt being processed
LOAD T4,PSEQ,(TPKT) ; Start of packet being processed
CAMN T1,T4 ; Quick check for exact duplicate
CAME T2,T3 ; (Faster than OVRLAP)
CAIA ; Have to use OVRLAP to be sure
TDZA T1,T1 ; Fake false return from OVRLAP
CALL OVRLAP ; See if QPKT has something PKT does not
JUMPN T1,REMCH9 ; Jump if so. Must keep both.
MOVE T1,QPKT ; This packet is extra baggage.
CALL DQ ; Remove it from the queue.
PUSH P,PKT
MOVE PKT,T1 ; Put pointer in standard place
MOVX T1,PT%TDR
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
CALL RETPKT ; Give up possibly full-size packet
POP P,PKT
SKIPA T1,[-1] ; Return true to say something was done
REMCH9: SETZ T1, ; Return false to say nothing was done
RESTORE
RET
; PRCRST Process a RESET packet
;PKT/ (Extended) Internet Packet Pointer
;TPKT/ (Extended) TCP Packet Pointer
;TCB/ (Extended) Locked connection block
;
; CALL PRCRST
;Ret+1: always
PRCRST: AOS RSTRCT ; Count RSTs received
LOAD T1,TSSYN,(TCB) ; Send state
LOAD T2,TRSYN,(TCB) ; Recv state
CAIN T1,FINSNT
CAIE T2,NOTSYN
JRST PRCRS1
; S-FINSNT, R-NOTSYN ; PKT always have ACK??
LOAD T1,PACKS,(TPKT) ; ACK Sequence from packet
CALL PRCACK ; Process it.
EXIT PRCRSX
PRCRS1: JRST PRCRS2 ; prevent hanging when SYNAGN forgets user open timeout
JN TSOPN,(TCB),PRCRS2 ; Jump if user thinks connection is open
JE TSPRS,(TCB),PRCRS2 ; Give error if not persistent
CALL SYNAGN ; Start over.
EXIT PRCRSX
PRCRS2: MOVX T1,EFP+↑D7 ; "Connection RESET"
CALL USRERR ; Tell user.
MOVX T1,EFP+↑D7 ; "Connection RESET"
CALL ABTCON ; Abort the connection. Flush bufs etc
PRCRSX: RET
; PRCWND Process Window Information from incoming packet
; We desire the most recently sent information to be that which is acted
; on. Since retransmitted packets have more current information than
; when they were originally transmitted, the packet sequence is not a
; good basis for deciding if a given packet has more recent info. In
; order to prevent lockups, window information must be processed out of
; sequence.
;(PKT/ (Extended) Internet Packet Pointer)
;TPKT/ (Extended) TCP Packet Pointer
;TCB/ (Extended) Locked connection block
;
; CALL PRCWND
;Ret+1: always.
PRCWND: LOAD T1,TSWND,(TCB) ; The current window
LOAD T2,PWNDO,(TPKT) ; The new window
STOR T2,TSWND,(TCB) ; Set into the TCB
JUMPN T1,PRCWNX ; Exit if window not closed previously
JUMPE T2,PRCWNX ; Or not now open
; Window opened
LOAD T3,PACKS,(TPKT) ; Get the ACK Sequence from the packet
LOAD T4,TSLFT,(TCB) ; and Send Left from TCB
CAME T3,T4 ; Re-request for Left?
EXIT PRCWNX ; No.
LOAD T1,QNEXT,<+TCBRXQ(TCB)>
CAIN T1,TCBRXQ(TCB) ; Retransmit queue empty?
EXIT PRCWNX ; Yes. No need for retranmitter to run
$SIGNL(RX,0) ; Make Retransmitter run now
PRCWNX: RET
; PRCACK Delete acknowledged send data
; Called from IP while processing incoming packets and by BG if it
; needs to fake an ACK for a FIN.
;T1/ ACKnowledge sequence
;TPKT/ (Extended) TCP Packet Pointer (for PRCWND)
;TCB/ (Extended) Locked Connection block
;
; CALL PRCACK
;Ret+1: always
PRCACK::LOCAL <ACKSEQ,LEFT,OLDR>
MOVEM T1,ACKSEQ ; Save in good place
LOAD T2,TSLFT,(TCB) ; Send Left
MOVEM T2,LEFT ; Save in safe place
EXCH T1,T2 ; Put in desired ACs for CHKWND below
LOAD T3,TSSEQ,(TCB) ; Get the current Send Sequence
ADDI T3,1
MODSEQ T3
CALL CHKWND ; ACKs something outstanding?
JUMPE T1,PRCACX ; Do no more if not
; Process Urgent
JE TSURG,(TCB),PRCAC0 ; Skip following if not in urgent send mode
MOVE T1,LEFT ; Send left
LOAD T2,TSURP,(TCB) ; Send urgent pointer
SUBI T2,1 ; Consider ACKd if ACK=URP
MODSEQ T2 ; Worry about 32-bit arithmetic
MOVE T3,ACKSEQ ; Number being acknowledged
CALL CHKWND ; See if URP is being ACKd
JUMPE T1,PRCAC0 ; Jump if not
SETZRO TSURG,(TCB) ; Leave urgent send mode
PRCAC0:
; Process window
MOVE OLDR,LEFT ; Old Send Left
LOAD T1,TSWND,(TCB) ; Old Send Window
ADD OLDR,T1 ; Compute Old Send Right
MODSEQ OLDR
CALL PRCWND ; Process Window info in packet
; (TSWND = PWNDO, maybe RX)
STOR ACKSEQ,TSLFT,(TCB) ; ACK Sequence is our Send Left
; Process unACKed SYN
LOAD T1,TSSYN,(TCB) ; Send State
CAIE T1,SYNSNT
JRST PRCAC2 ; No unACKd SYN to handle
MOVX T1,SYNCED
STOR T1,TSSYN,(TCB) ; Set fully synched state on send side
LOAD T2,TRSYN,(TCB) ; Recv state
CAIE T2,SYNRCV
JRST PRCAC1
STOR T1,TRSYN,(TCB) ; Make receive side open too
JN TTVT,(TCB),PRCA0A ; Avoid RA since TVT not assigned
$SIGNL(RA,0) ; Make RA remove any dangling SYN pkt
PRCA0A:
MOVX T1,OK ; General success code
CALL USREVT ; Pass the event to the user
PRCAC1: ; (No TVT error needs work here)
JE TRPP,(TCB),PRCAC2 ; If there is no partial pkt in RA
$SIGNL(RA,0) ; Make Reassembler run now
PRCAC2:
STOR ACKSEQ,TSLFT,(TCB) ; ACK Sequence is our Send Left
MOVEI T1,TCBRXQ(TCB) ; Retransmit queue
SETSEC T1,INTSEC ; Make extended address
MOVE T2,LEFT ; Old Left
MOVE T3,ACKSEQ ; New Left
SETZ T4, ; A send queue is being processed
CALL REMSEQ ; Delete ACKed packets
; Now that the new Send Window location and extent have been set,
; determine if the Packetizer should be started. This means there
; must be something waiting to be sent and window space to send it
; in and the connection state must be right.
;PRCAC3:
LOAD T1,TSSYN,(TCB) ; Get send state
CAIE T1,SYNCED ; Fully synchronized?
JRST PRCAC4 ; No. Don't start PZ
; Test for waiting output on TCP Virtual Terminal connection
JE TTVT,(TCB),PRCA34 ; Jump if not a TVT
LOAD T2,TVTL,(TCB) ; Get the line number of the TVt
JUMPE T2,PRCAC4 ; Jump if not assigned yet
CALL LCKTTY ; Lock TTY & NOINT
JUMPLE T2,PRCA33 ; Inactive or becoming active
PUSH P,T2 ; Save the line block addr
CALL TVTOSP ; Find out if anything waiting to go
POP P,T2 ; Recover addr of line block
SKIPA
PRCA33: MOVX T1,0 ; No TTY means no characters
PUSH P,T1 ; Save the count
CALL ULKTTY ; Unlock TTY & OKINT
POP P,T1 ; Get back the count
JUMPG T1,PRCA35 ; Something to be sent. See if OK
JRST PRCAC4 ; Nothing to be sent.
PRCA34:
; Test for waiting output on a normal TCP data connection
JN TSCB,(TCB),PRCA35 ; Jump if something wait to be sent
LOAD T1,QNEXT,<+TCBSBQ(TCB)>
CAIN T1,TCBSBQ(TCB) ; Any queued from user?
JRST PRCAC4 ; Nothing to be sent
PRCA35:
; Connection state is right and there is something waiting to be sent
LOAD T1,TSLFT,(TCB) ; Get new Send Left
LOAD T2,TSSEQ,(TCB) ; Get current Send Sequence
SUB T2,T1 ; Bytes used in send window
MODSEQ T2 ; (Must be .ge. 0)
LSH T2,2 ; 4 * Used
LOAD T3,TSWND,(TCB) ; Get new Send Window
IMULI T3,3 ; 3 * Offered window
CAMGE T2,T3 ; If Used/Offered .lt. 3/4
CALL FRCPKT ; Run PZ, but after RA
PRCAC4:
; See if packets which might have been made untransmittable due to
; Send Right moving backwards have now become transmittable. If so
; start the Retransmitter. This is due to the "PUSH problem"
; in which the sender has no idea of the size of the receive
; buffers and therefore cannot tell how many sequence number slots
; a PUSH will absorb.
LOAD T1,QNEXT,<+TCBRXQ(TCB)>
CAIN T1,TCBRXQ(TCB) ; Any packets on retransmit queue?
JRST PRCACX ; No
MOVE T1,LEFT ; Old Left before ACK
MOVE T2,OLDR ; Old right, before this ACK
LOAD T3,TSSEQ,(TCB) ; Current send sequence
CALL CHKWND ; Any pkts cutoff?
JUMPE T1,PRCACX ; Jump if not
MOVE T1,OLDR ; Right before this ACK,WND processed
MOVE T2,ACKSEQ ; New Left due to this ACK
LOAD T3,TSSEQ,(TCB) ; Current send sequence
CALL CHKWND ; See if ACK has exposed any pkts
JUMPE T1,PRCACX ; Jump if not
XMOVEI T1,RX ; What to signal -- the retransmitter
MOVX T2,0 ; When to run it -- now
CALL SIGNAL ; But after we finish here
PRCACX: RESTORE
RET
; PRCURG Process URGENT pointer from packet
;TCB/ (Extended) pointer to locked connection block
;(PKT/ (Extended) pointer to packet)
;TPKT/ (Extended) pointer to TCP portion of packet
;
; CALL PRCURG
;Ret+1: Always.
PRCURG: LOCAL <URGPTR>
LOAD URGPTR,PSEQ,(TPKT) ; Sequence number of packet
LOAD T1,PURGP,(TPKT) ; Offset to urgent pointer
ADD URGPTR,T1 ; Compute actual urgent pointer
MODSEQ URGPTR ; Reduce to the right number of bits
JN TRURG,(TCB),PRCUR1 ; Already in urgent receive mode?
STOR URGPTR,TRURP,(TCB) ; No. Set receive urgent pointer
SETONE TRURG,(TCB) ; Mark it as valid.
CALL USRURG ; Signal user of urgent data waiting
EXIT PRCURX
PRCUR1: LOAD T1,TRLFT,(TCB) ; Receive Left pointer
MOVE T2,URGPTR ; What packet says pointer is
LOAD T3,TRURP,(TCB) ; Current Urgent pointer
CALL CHKWND ; See if urgent pointer is "bigger"
JUMPN T1,PRCURX ; Nothing to do if not
STOR URGPTR,TRURP,(TCB) ; Update receive urgent pointer
PRCURX: RESTORE
RET
; PRCSYN Process SYN in incoming packet
;PKT/ (Extended) Internet Packet Pointer
;TPKT/ (Extended) TCP Packet Pointer
;TCB/ (Extended) Locked connection block
;
; CALL PRCSYN
;Ret+1: always
PRCSYN: LOAD T1,TRSYN,(TCB) ; Get receive state
CAIN T1,FINRCV ; Ignore SYN if FIN Received
EXIT PRCSYX
LOAD T2,TSSYN,(TCB) ; Get send state
CAIE T2,SYNCED ; Send sync established?
JRST PRCSY2 ; No.
; S-SYNCED (Recv'd ACK, maybe this packet)
MOVX T1,SYNCED
STOR T1,TRSYN,(TCB) ; Make the state "synchronized" (syn.syn)
MOVX T1,OK ; General success event code
CALL USREVT ; Tell the user connection is open now
JRST PRCSY3
; S-not synced (& not in this packet)
PRCSY2: MOVX T1,SYNRCV ; SYN Received state
STOR T1,TRSYN,(TCB) ; is new Receive state
LOAD T2,PWNDO,(TPKT) ; Extract the window
STOR T2,TSWND,(TCB) ; That is the (first) send window for us
PRCSY3:
LOAD T1,PSEQ,(TPKT) ; Get the packet sequence number
STOR T1,TRIS,(TCB) ; Save for filtering duplicate SYNs
STOR T1,TRURP,(TCB) ; Not in urgent receive mode
ADDI T1,1 ; Advance Recv.Left over SYN
MODSEQ T1
STOR T1,TRLFT,(TCB) ; That is the first Left for us
; If the Reassembler will not see this packet, get an ACK for it now.
; Otherwise, see to it that one is eventually generated.
LOAD T1,PIPL,(PKT) ; Total Internet packet length in octets
LOAD T2,PIDO,(PKT) ; Data offset in 32-bit words
LOAD T3,PTDO,(TPKT) ; TCP data offset in 32-bit words
ADD T2,T3 ; Compute total header length
ASH T2,2 ; In bytes
CAMLE T1,T2 ; Is there any data in TCP portion?
JRST PRCSY4 ; Yes. RA must see it.
LOAD T1,PCTL,(TPKT) ; Get word containing control flags
TXNE T1,<PFIN!PEOL>
JRST PRCSY4 ; RA must see these
CALL FRCPKT ; Force an ACK, now.
JRST PRCSY5
PRCSY4: MOVE T1,TCPRA0 ; Time to wait for RA
CALL ENCPKT ; Encourage an ACK in the future
PRCSY5: AOS SYNRCT ; Count SYNs received
PRCSYX: RET
; SNDRST Send a RESET Response to the Foreign TCP
;PKT/ (Extended) Internet Packet Pointer
;TPKT/ (Extended) TCP Packet Pointer
;TCB/ 0 or (Extended) connection block
;
; CALL SNDRST Beware of routing options
;Ret+1: always
SNDRST: STACKL <<ADR,4>>
LOCAL <PKTACK,ENDPKT>
PUSH P,TCB
PUSH P,PKT
PUSH P,TPKT
LOAD T1,PISH,(PKT) ; Extract source of packet
LOAD T2,PIDH,(PKT) ; Get number he knew me by
LOAD T3,PSP,(TPKT)
LOAD T4,PDP,(TPKT) ; and destination port
DMOVEM T1,ADR ; Swap into address block
DMOVEM T3,2+ADR
LOAD ENDPKT,PESEQ,(PKT) ; Get the end of the packet (plus 1)
LOAD T1,PACKS,(TPKT) ; Extract the ACK Sequence from PKT
MOVEM T1,PKTACK
; Now we have tucked away all we need from the incoming packet.
SETZB T1,TCB ; Min packet & no data & no TCB
XMOVEI T2,ADR ; Address block
CALL TCPIPK ; Get packet & initialize header
JRST SNDRSX ; Error. Other end will try again.
SETONE PRST,(TPKT) ; Set the RST bit
STOR ENDPKT,PACKS,(TPKT); Arrange to ACK all of the input packet
SETONE PACK,(TPKT) ; Set the ACK bit
MOVE T1,PKTACK ; ACK Sequence from packet
JUMPE TCB,SNDRS1 ; if there is no TCB
LOAD T1,TSSEQ,(TCB) ; Else use the right thing
SNDRS1:
STOR T1,PSEQ,(TPKT) ; As the Packet Sequence number
MOVE T1,TODCLK ; "Now"
STOR T1,PTG,(PKT) ; Store as Time Generated
CALL TCPCKS ; Compute TCP packet checksum
STOR T1,PTCKS,(TPKT) ; Set into packet
MOVX T1,PT%XX2 ; Fake OP
SKIPN TCB
MOVX T1,PT%XX7
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes, Call the packet printer
MOVX T1,PT%TIR
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
;SNDRS3:
AOS RSTSCT ; Count errors sent
AOS PZPKCT ; Count Packetized packets
AOS OPPKCT ; Count Output packets
XMOVEI T1,OPDLAY ; Select OP Delay Histogram
SKIPE STATF ; Avoid overhead if not taking stats
CALL TSTAMP ; Process the timestamp
CALL SNDGAT ; Send it to gateway. (NB: PPROG is 0)
SNDRSX: POP P,TPKT
POP P,PKT
POP P,TCB
RESTORE
RET
; REMSEQ Remove packets from a queue which are between Left and Right
;T1/ (Extended) Queue head pointer
;T2/ Left
;T3/ Right
;T4/ Receive Queue Flag (0 is SEND is TCBRXQ)
;TCB/ (Extended) Locked connection block
;
; CALL REMSEQ
;Ret+1: always
REMSEQ::STACKL <NEXTRX,RECVF>
LOCAL <Q,LEFT,RIGHT,NEXT>
PUSH P,PKT
PUSH P,TPKT
DMOVEM T1,Q ; T1,T2 to Q,LEFT
MOVEM T3,RIGHT ; T3 to RIGHT
MOVEM T4,RECVF
HRLOI T1,377777 ; Infinity
MOVEM T1,NEXTRX ; is first quess at next RX time
LOAD NEXT,QNEXT,(Q) ; Get first thing on Queue (if any)
SETSEC NEXT,INTSEC ; Make extended address
REMSE1: MOVE PKT,NEXT ; Get the current pkt to standard place
LOAD NEXT,QNEXT,(PKT) ; Set for next time
SETSEC NEXT,INTSEC ; Make extended address
CAMN PKT,Q ; Is this the queue Head?
JRST REMSE7 ; Yes. Done. Whole queue scanned.
MOVE T1,LEFT
LOAD T2,PESEQ,(PKT) ; End of packet plus one
SUBI T2,1 ; Seq. Num of last byte in packet
MODSEQ T2
MOVE T3,RIGHT
CALL CHKWND ; Is end of packet within the window?
JUMPN T1,REMSE2 ; Yes. Go delete the packet.
LOAD T1,PXT,(PKT) ; Get the Transmit time
LOAD T2,PRXI,(PKT) ; and current Retransmit interval
ADD T1,T2 ; Time of next retransmit
CAMG T1,NEXTRX ; MIN with last time of next RX
MOVEM T1,NEXTRX
JRST REMSE1 ; Continue scanning the queue
REMSE2: MOVE T1,PKT
CALL DQ ; Dequeue the packet from the queue
SKIPE RECVF ; Processing receive packet queue?
JRST REMSE6 ; Yes
; Send Queue
LOAD T1,PIDO,(PKT) ; Number of words in the IN header
XMOVEI TPKT,PKTELI(PKT) ; Pointer to Internet portion
ADD TPKT,T1 ; Pointer to TCP portion
; ACK of FIN
JE PFIN,(TPKT),REMSE4 ; Skip this part if not ACK of FIN
MOVX T1,NOTSYN ; Not Synchronized state (dead)
STOR T1,TSSYN,(TCB) ; Set into TCB
LOAD T1,TRSYN,(TCB) ; Get receive state
CAIE T1,NOTSYN ; Also closed?
JRST REMSE4
SKIPE INTSCR ; Running in Secure mode?
CALL SCRCLS ; Yes. Send a Secure Close Option
MOVX T1,XLP+↑D3 ; "CLOSED" event code
CALL USREVT ; Pass the word to the user
REMSE4:
; Update estimate of round trip time
; Instead of discarding RX'd packets, include them in the sum...
; If the original is not being ACKed, then a packet was lost so
; something is probably overloaded, slow down RX in either case
; JN PRXD,(PKT),REMSE6 ; No statistics on RX'd packets
MOVE T1,TODCLK ; "Now"
LOAD T2,PTG,(PKT) ; Time packet was originally generated
SUB T1,T2 ; How long it took to ACK it
JE <TRXPN,TRXPD>,(TCB),REMSE5 ; Newer algorithm
LOAD T2,TMNRT,(TCB) ; Min Round Trip time
CAMGE T1,T2 ; Is this one shorter?
STOR T1,TMNRT,(TCB) ; Save new min
LOAD T2,TMXRT,(TCB) ; MAX Round Trip time
CAMLE T1,T2 ; Is this longer?
STOR T1,TMXRT,(TCB) ; Save new max
JRST REMSE6
REMSE5: LOAD T4,TSMRT,(TCB) ; Current estimate
MOVE T3,T4
LSH T3,2
CAMLE T1,T3 ; If packet time is greater than
JFCL ;JRST REMSE6 ; 4*current estimate, discard it
MOVX T2,1
MOVNI T3,@TCPRXF ; Positive scale factor
LSH T2,(T3) ; Scaled 1.0
SUB T2,TCPRXS ; Scaled (1-alpha)
IMUL T1,T2 ; RTT*(1-alpha)
IMUL T4,TCPRXS ; SRTT*alpha
ADD T1,T4
LSH T1,@TCPRXF ; New estimate of round trip time
STOR T1,TSMRT,(TCB)
REMSE6:
; Both Send and Receive Queues
SETZRO PPROG,(PKT) ; Say no need for Pkt at program level
JN PINTL,(PKT),REMSE1 ; Jump if interrupt will do the INTRBF
MOVX T1,PT%TDR
SKIPN RECVF
MOVX T1,PT%TDX
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
CALL RETPKT ; Return the storage to free area
JRST REMSE1 ; Go look at more of the queue
REMSE7: SKIPE RECVF ; Processing send retransmit queue?
JRST REMSEX ; No
CAMN PKT,NEXT ; Queue empty now?
JRST REMSE8 ; Yes.
XMOVEI T1,RX ; Select the Retransmitter
MOVE T2,NEXTRX ; Computed next retransmit time
SUB T2,TODCLK ; Convert to increment
CALL SIGNAL ; Make the retransmitter run then
JRST REMSEX
REMSE8: MOVEI T1,TCBQRX(TCB)
SETSEC T1,INTSEC
SKIPE (T1)
JFCL ; CALL DQTASK
REMSEX: POP P,TPKT
POP P,PKT
RESTORE
RET
; ABTCON Abort a connection
; Clears queues and forces send and receive buffers back to the user
; with the (argument) Code. The connection is set to Not Synchronized.
; Called both from PRCRST and from the ABORT JSYS.
;TCB/ (Extended) Locked Connection Block
;T1/ Event Code (ELP+↑D14 - reset)
; (ELT+↑D4 - no free TVTs)
; CALL ABTCON
;Ret+1: always
ABTCON::LOCAL <CODE>
PUSH P,PKT
MOVEM T1,CODE
; Buffers flushed via USRBFE/F are placed onto the TCPDBQ by BFRDUN if
; the have wait bits assigned (for wait or interrupt); others are
; returned directly to free storage.
CALL FLSSBF ; Flush SEND buffers
MOVE T1,CODE
CALL FLSRBF ; Flush RECV buffers
; Flush packets from Retransmission Queue
ABTCOA: LOAD T1,QNEXT,<+TCBRXQ(TCB)> ; Get first thing on Retrans Q
CAIN T1,TCBRXQ(TCB) ; Is that the head itself?
JRST ABTCOB ; Yes. The queue is now empty.
SETSEC T1,INTSEC ; Make extended address
CALL DQ ; Remove from the Retransmission queue
SETZRO PPROG,(T1) ; Program level now has no claim on PKT
JN PINTL,(T1),ABTCOA ; Jump if INTRBF will return the space
MOVE PKT,T1 ; Put pointer in right place for RETPKT
CALL RETPKT
JRST ABTCOA
; Flush packets from Received Packet Queue
ABTCOB: LOAD T1,QNEXT,<+TCBRPQ(TCB)>
CAIN T1,TCBRPQ(TCB)
JRST ABTCOC
SETSEC T1,INTSEC ; Make extended address
CALL DQ
MOVE PKT,T1
CALL RETPKT
JRST ABTCOB
ABTCOC:
; Flush any Partially Filled Packet
NOSKED
LOAD PKT,TSCPK,(TCB) ; Possible packet
SETZRO TSCPK,(TCB) ; is gone
OKSKED
SKIPE PKT ; IF have one
CALL RETPKT ; release it
; Collect buffers for this TCB from TCPBDQ and place them on TCPIDQ.
; Then release them all.
NOSKED ; Prevent user from snatching bufs
MOVE T1,TCPIDQ
CALL ABTCO1 ; Collect up all dead ones
OKSKED
MOVE T1,TCPIDQ
MOVE T2,T1
CALL CLEARQ ; Return all to free storage
; Force state to NOT.NOT
MOVX T1,NOTSYN
STOR T1,TSSYN,(TCB) ; Set Send state to Not Synchronized
STOR T1,TRSYN,(TCB) ; Set Recv state to Not Synchronized
; Notify the user
MOVE T1,CODE
CALL USREVT ; Pass the event to the user
; Maybe need a secure close
SKIPE INTSCR ; Running in Secure mode?
CALL SCRCLS ; Send a Secure Close Option
POP P,PKT
RESTORE
RET
; ABTCO1(Q) Release buffers from Buffer Done Queue
;T1/ (Extended) Pointer to a queue head
;TCB/ (Extended) Locked connection block
;NOSKED How do we know they are finished??
; CALL ABTCO1
;Ret+1: Always. No buffers on DEADBQ owned by TCB; BIDX bits released
ABTCO1: LOCAL <NXTBFR,DEADBQ>
PUSH P,BFR
MOVEM T1,DEADBQ
MOVE NXTBFR,TCPBDQ ; Pointer to queue head
ABTCO2: MOVE BFR,NXTBFR
LOAD NXTBFR,QNEXT,(BFR) ; Get next item on the list
SETSEC NXTBFR,INTSEC ; Make extended address
CAMN BFR,TCPBDQ ; Back to head means done
JRST ABTCOX
LOAD T1,BTCB,(BFR) ; Get owning TCB
SETSEC T1,INTSEC ; Make extended address
CAME T1,TCB ; It is this connection?
JRST ABTCO2 ; Go try next
MOVE T1,BFR ; Pointer to the item
CALL DQ ; Remove it
LOAD T1,BIDX,(BFR) ; Get the wait bit index
CALL RELWTB ; Release it
MOVE T1,BFR ; Pointer to the block again
MOVEI T2,DEADBQ ; Where to stash the buffer for later
CALL NQ ; Release when not NOSKED
JRST ABTCO2
ABTCOX: POP P,BFR
RESTORE
RET
; SYNAGN Return a connection to Synchable state
;TCB/ (Extended) Locked Connection Block
;
; CALL SYNAGN
;Ret+1: always
SYNAGN: PUSH P,BFR
PUSH P,PKT
CALL RSTADR ; Restore wild address fields
; Moving Send Left to Send Sequence makes the next incarnation of this
; connection different from the one which has just failed and is
; getting resynched. Thus, RSTs caused by old copyies of the original
; SYN will be unacceptable and will not wipe out the new incarnation.
LOAD T1,TSSEQ,(TCB) ; Send sequence = SYN + DATA
STOR T1,TSLFT,(TCB) ; Where to begin next time.
MOVX T1,SYNABLE
STOR T1,TRSYN,(TCB) ; Reset the Recv state
LOAD BFR,TRCB,(TCB) ; Get current receive buffer
JUMPE BFR,SYNAG1 ; Jump if no receive current buffer
SETSEC BFR,INTSEC ; Make extended address
SETZRO TRCB,(TCB) ; Forget about it
CALL RSTBFR ; Reset it to virgin state (hard to do)
MOVE T1,BFR ; What to NQ
MOVEI T2,TCBRBQ(TCB) ; The receive buffer queue
SETSEC T2,INTSEC ; Make extended address
LOAD T2,QNEXT,(T2) ; First thing on the queue
SETSEC T2,INTSEC ; Make extended address
CALL NQ ; Make the recycled buffer first again
SYNAG1:
SETZRO TRPP,(TCB) ; Forget about partially processed PKT
SYNAG2: LOAD T1,QNEXT,<+TCBRPQ(TCB)> ; Receive Packet Queue
CAIN T1,TCBRPQ(TCB) ; Empty now?
JRST SYNAG3 ; Yes
SETSEC T1,INTSEC ; Make extended address
CALL DQ ; Dequeue the packet
MOVE PKT,T1 ; Put in standard place
CALL RETPKT ; Return possibly full size packet
JRST SYNAG2
SYNAG3:
MOVX T1,SYNABL
STOR T1,TSSYN,(TCB) ; Reset Send state
LOAD BFR,TSCB,(TCB) ; Get current send buffer
JUMPE BFR,SYNAG4 ; Jump if none
SETSEC BFR,INTSEC ; Make extended address
SETZRO TSCB,(TCB) ; Forget there was one
CALL RSTBFR ; Reset the buffer
MOVE T1,BFR
MOVEI T2,TCBSBQ(TCB) ; Send buffer queue
SETSEC T2,INTSEC ; Make extended address
LOAD T2,QNEXT,(T2) ; First thing on the queue
CALL NQ ; Make recycled buffer first again
SYNAG4:
MOVEI T1,TCBRXQ(TCB) ; Retransmit queue
SETSEC T1,INTSEC ; Make extended address
MOVE T2,T1 ; Almost never full size packets!
CALL CLEARQ ; Clear it
JE TSPRS,(TCB),SYNAGX ; Check if this end is initiator
JN <TWLDN,TWLDT,TWLDP>,(TCB),SYNAGX ; Don't send if no 4N host
MOVE T1,TCPSY0 ; 2 second delay to prevent loop if
CALL DLAYPZ ; Foreign TCB non-x and RST causing us to loop
SYNAGX: POP P,PKT
POP P,BFR
RET
; RSTADR(TCB) Restore wild address fields
; A delayed duplicate may cause a foreign TCP to emit an RST packet to
; kill what it thinks is a half-open connection here. If in fact the
; connection has been closed and deleted, there may be a listening
; TCB which CHKADD will find and bind to the source of the RST.
; Subsequently the TCP will just flush the RST and will not emit any
; response to it. This routine is called to undo the temporary binding.
;TCB/ (Extended) Locked connection block
;
; CALL RSTADR
;Ret+1: Always.
RSTADR: LOAD T1,TOPLH,(TCB) ; Restore original local host
STOR T1,TLH,(TCB)
LOAD T1,TOPFH,(TCB) ; Restore original foreign host
STOR T1,TFH,(TCB)
LOAD T1,TOPFP,(TCB) ; Restore original foreign port
STOR T1,TFP,(TCB)
SETZRO TIPOR,(TCB) ; No received IP option count
SETZM TCBIR(TCB) ; Nor data
SETZRO TTPOR,(TCB) ; No received TCP option count
SETZM TCBTR(TCB) ; Nor data
SETO T1, ; Don't change options
CALL TCPUOP ; But re-merge
SETZ T1,
CALL TCPMXP ; Undo max packet size
RET
; IPINI Initialize IP process block
; CALL IPINI
;Ret+1: Always
IPINI:: LOCAL <PRC>
XMOVEI PRC,IP ; Pointer to process block
; Following are guards against really bad things
SETZM PRCQ(PRC) ; Be sure queue is empty.
SETZM PRCLCK(PRC) ; Should never try to lock IP lock!
SETZM PRCWAK(PRC) ; Be sure to run it promptly!
SETZM PRCQOF(PRC) ; Clear unused cells
SETZM PRCWOF(PRC)
XMOVEI T1,INPROC ; Routine address
MOVEM T1,PRCROU(PRC) ; Set into the control block
XMOVEI T1,IPRNCT ; Run counter address
MOVEM T1,PRCRNC(PRC)
XMOVEI T1,IPUSE ; CPU usage meter
MOVEM T1,PRCTMR(PRC)
MOVX T1,QSZ ; Size of a queue head
CALL GETBLK ; Queue head must be in Internet section
JUMPE T1,IPINIX ; Lose, need better way (lose memory)
MOVEM T1,TCPIDQ ; Save pointer to it
CALL INITQ ; Initialize it
HRROI T1,-1
IPINIX: RESTORE
RET
TNXEND
END